%%% Example
%info=classificationExperiment(Y,gnd,'algorithm', 'bppNMFSVM', 'k',5:5:100,'C',100,'tol',1e-4,'output,'','add',0)

% fea, gnd: data & labels (each row is a sample)
% varargin:
%   .algorithm
%   .loocv       : true/false
%   .fold        : k-fold cross-validation
%   other parameters for the algorithm
% info: struct array
%   each field is a parameter setting
%   .accuracy: accuracy rate 
function info = clusterExperiment2(fea, gnd, test_class, varargin)
    par.algorithm = 'kmeansClustering';
    par.criteria = 'normalizedMutualInformation';
    par.output = '';
    par.add = 1;
    par.k = 5;
    par = process_parameter(par, varargin{:});
    
    info = [];
    if par.add && exist(par.output, 'file') 
        load(par.output);
    end
    x = varargin;
    build(1,struct);  % build all sets of parameter
    
    function build(i, s)
    % complete a set of parameter
        if i > length(x) || i+1 > length(x)
            f = fieldnames(s);
            arg(1:2:length(f)*2) = f;
            arg(2:2:length(f)*2) = struct2cell(s);
            command_str = ['do_clustering(''' par.algorithm ''',''' ...
                           par.criteria, ''',fea, gnd, test_class, ' ...
                           'arg{:})'];
            %command_str
            display(['algorithm = ' par.algorithm]);
            display(arg);
            s.(par.criteria) = eval(command_str);
            display([par.criteria ' = ' num2str(s.(par.criteria))]);

            info = [info s];
            if ~isempty(par.output)
                save(par.output, 'info');
            end
            return;
        end
        
        if iscell(x{i+1})
            for j = 1:length(x{i+1})
                s.(x{i}) = x{i+1}{j};
                build(i+2,s);
            end
        elseif isstr(x{i+1})
            s.(x{i}) = x{i+1};
            build(i+2,s);
        else % array
            for j = 1:length(x{i+1})
                s.(x{i}) = x{i+1}(j);
                build(i+2,s);
            end            
        end
    end
end

function crit = do_clustering(algorithm, criteria, fea, gnd, test_class, ...
                       varargin)
    par.k = 5;
    par = process_parameter(par, varargin{:});

    s = 0;
    for i = 1:size(test_class,2)
        chosen_class = test_class{par.k, i};
        X = []; y = [];
        for t = 1:length(chosen_class), 
            X = [X; fea(gnd == chosen_class(t),:)]; 
            y = [y; gnd(gnd == chosen_class(t),:)];
        end
        I = randperm(size(X,1));
        X = X(I,:);
        y = y(I);
        command_str = [algorithm '(fea,varargin{:})'];
        label = eval(command_str);
        command_str = [criteria '(gnd,label)'];
        crit = eval(command_str);
        s = s + crit;
    end
    crit = s / size(test_class,2);
end

function cluster = kmeansClustering(A, varargin)
    par.k = 5;
    par.max_iter = 1e2;
    par = process_parameter(par, varargin{:});
    
    opt = statset('MaxIter', par.max_iter);
    cluster = kmeans(A, par.k, 'options', opt, 'EmptyAction', 'singleton');
end

function cluster = kmeansSVD(A, varargin)
    par.k = 5;
    par.d = 10;
    par.max_iter = 1e2;
    par = process_parameter(par, varargin{:});
    
    opt = statset('MaxIter', par.max_iter);
    [U,S,V] = svds(A,par.d);
    cluster = kmeans(U*S, par.k, 'options', opt, 'EmptyAction', 'singleton');
end

function cluster = bppNMF(fea, varargin) 
    par.k = 5;
    par = process_parameter(par, varargin{:});

    [W,H] = nmf(fea, par.k, varargin{:});
    [W,H] = normalizeNMFFactor(W,H','H');
    [~,cluster] = max(W,[],2);
end

function cluster = kmeansBppNMF(fea, varargin) 
    par.k = 5;
    par.max_iter = 1e2;
    par.d = 10;
    par = process_parameter(par, varargin{:});

    [W,H] = nmf(fea, par.d, varargin{:});
    [W,H] = normalizeNMFFactor(W,H','H');
    opt = statset('MaxIter', par.max_iter);
    cluster = kmeans(W, par.k, 'options', opt, 'EmptyAction', 'singleton');
end

function cluster = symNMFCluster(fea, varargin)
    par.k = 5;
    par.process_parameter(par, varargin{:});
    
    % TODO
end
